//Serão consideradas palavras apenas as sequencias de caracteres compostas por letras.
//Os caracteres " ,./?'\";:|^-!$#@`~*&%)(+=_}{][\n\t\\" serão considerados separadores pela funcao strtok
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>

#ifdef _OPENMP
#include <omp.h>
#define NTHREADS 4
#endif

//Definicao do tamanho de cada palavra e de cada bloco de leitura
#define BLOCK_SIZE 300
#define WORD_SIZE 100

void strRev(char *);
void strUpper(char *);
void parser(char *, char **, int *);
int chkPal(char *);
int crivo(char *);

int main(int argc, char *argv[])
{
	//Variáveis
	int cont = 0, cont2 = 0, i, n = 0;
	double t0, tf, nT = 0.0, wT = 0.0, rT = 0.0;
	char buffer[BLOCK_SIZE], **palavras;
	FILE *fpin;

	//Abre o arquivo
	if((fpin=fopen(argv[1], "r")) == NULL)
	{	
		printf("Erro ao abrir o arquivo %s.\n", argv[1]);
		return -1;
	}
	
	omp_set_num_threads(NTHREADS);
	
	//Pega um bloco de texto
	//Faz parse (pega cada palavra)
	//Checa palindromos, conta tempo
		//Caso palíndromo, testa se é primo, conta tempo
	
	while(!feof(fpin))
	{	
		t0 = omp_get_wtime();
		fread(buffer, BLOCK_SIZE, 1, fpin);
		tf = omp_get_wtime();
		rT += tf - t0;
		n = 0;
		char* token = strtok(buffer, " ,./?'\";:|^-!$#@`~*&%)(+=_}{][\n\t\\");
		palavras = (char**) malloc (sizeof(char*));
		while(token != NULL)
		{
			palavras[n] = strdup(token);
			token = strtok(NULL, " ,./?'\";:|^-!$#@`~*&%)(+=_}{][\n\t\\");
			n++;
			palavras = (char**) realloc (palavras, ((n+1)*sizeof(char*)));
		}
		#pragma omp parallel for num_threads(4) reduction(+:cont,cont2,wT,nT)
	  	for(i = 0 ; i < n ; i++)
		{
			t0 = omp_get_wtime();
			if(chkPal(palavras[i]))
			{
				tf = omp_get_wtime();
				wT += tf - t0;			
				cont++;
				t0 = omp_get_wtime();
				if(crivo(palavras[i]))
				{
					tf = omp_get_wtime();
					nT += tf - t0;
					cont2++;
				}
			}
		}
		free(palavras);
	}
	fclose(fpin);
	printf("Tempo para ler palavras: %lfs\nTempo para testar palavras: %lfs\nTempo para testar primos: %lfs\nNumero de palindromos: %d\nNumero de palindromos primos: %d\n", rT, wT, nT, cont, cont2);
	return 0;
}

void strRev(char *str)
{
	//Baseado no algoritmo XOR SWAP
	// X := X XOR Y
	// Y := X XOR Y
	// X := X XOR Y
	char *p1, *p2;
	for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) //p1 vai do começo até o meio da string e p2 vai do fim até o meio
	{
		//Swap
		*p1 ^= *p2;
		*p2 ^= *p1;
		*p1 ^= *p2;
	}
}

int chkPal(char *str)
{
	char aux[WORD_SIZE], aux2[WORD_SIZE];
	strcpy(aux2,str);
	//Inverte a palavra. Se for igual a palavra nao invertida, e palíndromo
	if(strlen(str) > 1)
	{
		strUpper(aux2);
		strcpy(aux,aux2);
		strRev(aux);
		if(strcmp(aux2,aux) == 0 )
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	else
	{ 
		return 0;
	}
}

void strUpper(char *str)
{
	//Passa a palavra para maiuscula, eliminando acentos, etc.
	int i;
	for (i = 0 ; i < strlen(str) ; i++)
	{
		str[i] = toupper(str[i]);
	}
}

int crivo(char palavra[])
{
	//Aplicação do crivo de erastotenes sobre as somas dos valores ASCII das palavras
	int i, divisor, divisores, num=0;
	float max;

	for (i=0;i<strlen(palavra);i++) 
	{
		num += (int)palavra[i];
	}

	if(num == 1 || num == 2 || num == 3)
	{
		return 1;
	}
	else if(num % 2 == 0)
	{
		return 0;
	}
	else
	{
		divisor = 2;
		divisores = 0;
		max = sqrt(num);
		while(divisor <= max && divisores == 0)
		{
			if(num % divisor == 0)
			{
				divisores++;
			}
			divisor++;
		}
		if(divisores != 0)
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
}
